package com.liuzhilong.infrastructure.framework.netty.api.standard.handler;

import com.liuzhilong.infrastructure.framework.netty.api.standard.command.Command;
import com.liuzhilong.infrastructure.framework.netty.api.standard.processer.ProcessorHolder;
import io.netty.channel.*;
import lombok.extern.slf4j.Slf4j;
import com.liuzhilong.infrastructure.framework.netty.api.standard.codec.CommandDecoder;

/**
 * server的处理器。
 * 共享的 内部使用{@link ServerHandler#getProcessorHolder} 作为处理器。
 * 这个handler要与{@link CommandDecoder} 配合使用
 * 配合使用
 *
 */
@ChannelHandler.Sharable
@Slf4j
public class ServerHandler extends ChannelInboundHandlerAdapter {

    private ProcessorHolder processorHolder=new ProcessorHolder();
    public ProcessorHolder getProcessorHolder() {
        return processorHolder;
    }

    /**
     * 处理读取事件。
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        processorCommand(ctx, (Command) msg);
    }

    private void processorCommand(ChannelHandlerContext channelHandlerContext, Command command){
        getProcessorHolder().getProcessor(command.getType()).processor(channelHandlerContext,command);
    }



    /**
     * 异常处理 如果发生异常，关闭channal，并打印log。
     * 本次处理完毕之后，就不往下传播了。
     * @param ctx channel handler context
     * @param cause cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("exceptionCaught : {}",cause.getMessage(), cause);
        ctx.channel().close();
    }




    /**
     * 当 Channel 的可写状态发生改变时被调用。
     * 用户可以确保写操作不会完成 得太快（以避免发生 OutOfMemoryError）
     * 或者可以在 Channel 变为再 次可写时恢复写入。
     * 可以通过调用Channel的isWritable()方法来检测 Channel的可写性。
     * 与可写性相关的阈值可以通过Channel.config(). setWriteHighWaterMark()和Channel.config().setWriteLowWater- Mark()方法来设置
     * @param ctx  channel handler context
     * @throws Exception
     */
    @Override
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
        Channel ch = ctx.channel();
        ChannelConfig config = ch.config();

        if (!ch.isWritable()) {
            if (log.isWarnEnabled()) {
                log.warn("{} is not writable, over high water level : {}",
                        new Object[]{ch, config.getWriteBufferHighWaterMark()});
            }

            config.setAutoRead(false);
        } else {
            if (log.isWarnEnabled()) {
                log.warn("{} is writable, to low water : {}",
                        new Object[]{ch, config.getWriteBufferLowWaterMark()});
            }
            config.setAutoRead(true);
        }
    }


}
